import type { FileChunk, Md5ProgressHandler } from './types';

import { MD5Utils } from './utils';

/**
 * 文件分片打包器
 */
export class FileChunkPacker {
  /**
   * 获取分片大小
   */
  get getChunkSize(): number {
    return this.chunkSize;
  }
  /**
   * 获取文件MD5
   */
  get getFileMd5(): string {
    return this.fileMd5;
  }
  /**
   * 获取文件名
   */
  get getFileName(): string {
    return this.fileName;
  }
  /**
   * 获取文件总大小
   */
  get getTotalSize(): number {
    return this.totalSize;
  }
  private readonly chunkSize: number;

  private readonly file: File;

  private fileMd5: string = '';

  private readonly fileName: string;

  private readonly totalSize: number;

  constructor(file: File, chunkSize: number) {
    this.file = file;
    this.chunkSize = chunkSize;
    this.fileName = file.name;
    this.totalSize = file.size;
  }

  /**
   * 计算文件MD5
   */
  async calculateFileMd5(onProgress?: Md5ProgressHandler): Promise<string> {
    if (!this.fileMd5) {
      this.fileMd5 = await MD5Utils.calcFile(
        this.file,
        8 * 1024 * 1024,
        onProgress,
      );
    }
    return this.fileMd5;
  }

  /**
   * 创建文件分片迭代器 - 优化内存使用
   */
  async *createChunkIterator(): AsyncGenerator<FileChunk, void, undefined> {
    const totalChunks = this.getChunkCount();

    for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {
      const offset = chunkIndex * this.chunkSize;
      const end = Math.min(offset + this.chunkSize, this.totalSize);
      const currentChunkSize = end - offset;

      try {
        // 读取当前分片数据
        const chunkBlob = this.file.slice(offset, end);

        // 检查内存使用情况
        if (currentChunkSize > 50 * 1024 * 1024) {
          // 50MB
          console.warn(
            `处理大分片 (${currentChunkSize / (1024 * 1024)}MB), 可能影响性能`,
          );
        }

        const chunkData = await chunkBlob.arrayBuffer();

        // 验证实际读取的数据大小是否与期望一致
        if (chunkData.byteLength !== currentChunkSize) {
          throw new Error(
            `分片数据大小不匹配: 期望 ${currentChunkSize} 字节，实际 ${chunkData.byteLength} 字节`,
          );
        }

        // 计算分片MD5
        const chunkMd5 = await MD5Utils.calcArrayBuffer(chunkData);

        yield {
          chunkIndex,
          chunkOffset: offset,
          chunkData,
          chunkMd5,
        };

        // 手动触发垃圾回收提示（浏览器会自动决定是否执行）
        if (typeof window !== 'undefined' && (window as any).gc) {
          try {
            (window as any).gc();
          } catch {
            // 忽略 gc 调用失败
          }
        }
      } catch (error) {
        console.error(`处理分片 ${chunkIndex} 时发生错误:`, {
          chunkIndex,
          offset,
          currentChunkSize,
          error: error instanceof Error ? error.message : String(error),
        });
        throw new Error(
          `分片 ${chunkIndex} 处理失败: ${error instanceof Error ? error.message : '未知错误'}`,
        );
      }
    }
  }

  /**
   * 获取所有分片（用于小文件或特殊场景）
   */
  async getAllChunks(): Promise<FileChunk[]> {
    const chunks: FileChunk[] = [];
    for await (const chunk of this.createChunkIterator()) {
      chunks.push(chunk);
    }
    return chunks;
  }

  /**
   * 获取分片数量
   */
  getChunkCount(): number {
    return Math.ceil(this.totalSize / this.chunkSize);
  }
}
